package ls.vuecp.gui.toolbar;

import java.awt.Cursor;
import java.awt.event.ActionEvent;
import java.awt.event.InputEvent;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Map;
import java.util.ResourceBundle;
import java.util.Vector;

import javax.swing.AbstractAction;
import javax.swing.AbstractButton;
import javax.swing.Icon;
import javax.swing.JLabel;
import javax.swing.JPanel;


import tufts.vue.DEBUG;
import tufts.vue.VUE;
import tufts.vue.VueResources;

public class VueTool extends AbstractAction 
{
	// +ls @20150928;
	// this actually includes all tool resource, but not the tool itself;
	
	// action = this;
	// button = mLinkedButton(AbstractButton);
	// panel = mToolPanel; 
	// relationsip : this->mLinkdButton->mToolPanel;
	
	// unforturnately, the PaletteButton is extends JRadioButton;
	// I don't think it's a good idea that: fixed bound to Radio Button, can't others;
	// !!! the more extends, the less flexibility;
	// !!! constructured by combination, not by extend;
	
	private static final org.apache.log4j.Logger Log = org.apache.log4j.Logger.getLogger(VueTool.class);
    
    private static final Map<Class<? extends VueTool>, VueTool> InstanceMap = new HashMap();
    private static final java.util.List<VueTool> mAllTools = new java.util.ArrayList();
	
    public VueTool()
    {
    	super();
    	
    	VueTool old = InstanceMap.put(getClass(), this);
    	mAllTools.add(this);
    }
    
    protected String mID = null;
    protected String mToolName = null;
    protected String mToolTipText = null;
      
    protected Icon mRawIcon;
    protected Icon mUpIcon;
    protected Icon mDownIcon;
    protected Icon mSelectedIcon;
    protected Icon mDisabledIcon;
    protected Icon mRolloverIcon;
    protected Icon mMenuItemIcon;
    protected Icon mMenuItemSelectedIcon;
    protected Icon mOverlayUpIcon;
    protected Icon mOverlayDownIcon;
 
    protected char mShortcutKey = 0;
    
    protected int mActiveWhileDownKeyCode = 0;
    
    protected java.awt.Cursor mCursor;
    
    protected HashMap mResourceMap = new HashMap();

    protected AbstractButton mLinkedButton;

    private boolean isTemporary;
    private JPanel mToolPanel;

    protected Vector mSubToolIDs = new Vector();
    protected Map<String,VueTool> mSubToolMap = new HashMap();    
    protected VueTool mParentTool = null;
    protected VueTool mSelectedSubTool = null;
    
    
    /** @return list of all tool instances  */
    public static java.util.List<VueTool> getTools() 
    {
        return mAllTools;
    }
    public static VueTool getInstance(Class<? extends VueTool> clazz) 
    {
        final VueTool tool = InstanceMap.get(clazz);
        if (tool == null)
            tufts.Util.printStackTrace("failed to find tool instance of type: " + clazz);
        return tool;
    }
    
    public void actionPerformed(ActionEvent pEvent) 
    {
    	System.out.println("[VueTool]."+this.mID+",actionPerformed().");
		
        if (DEBUG.TOOL) System.out.println("\n" + this + " " + pEvent);
		
        // notify parent;
        VueTool parent = this.getParentTool();
        if (parent != null)
            parent.setSelectedSubTool(this);
        if (parent != null) 
        {
            //if (DEBUG.Enabled) Util.printStackTrace("actionPerformed tool switch to " + parent + " from " + VueToolbarController.getController().getActiveTool());
            parent.handleToolSelection(true, VueToolbarController.getController().getActiveTool());
        }
        
        // notify controller;
        // now it do nothing;
        if (this.hasSubTools())
            VueToolbarController.getController().handleToolSelection(this.getSelectedSubTool());
        else
            VueToolbarController.getController().handleToolSelection(this);
    }
	
    

    private void handleToolSelection(boolean b, VueTool activeTool) {
	// notified by subTool;
    	System.out.println("[VueTool]."+this.mID+",handleToolSelection("+b+","+activeTool.getID()+")");
	}

	public boolean isActive() 
    {
        return VueToolbarController.getActiveTool() == this;
    }

    public String getID() {
        return mID;
    }
	
    public void setID(String pID ) {
        mID = pID;
        //System.out.println("Initializing tool " + this);
        ResourceBundle rb = VueResources.getBundle();
        Enumeration e = rb.getKeys();
        while (e.hasMoreElements()) {
            String key = (String) e.nextElement();
            if (key.startsWith(pID)) {
                String localKey = key.substring(mID.length()+1, key.length());
                //System.out.println("\tkey: " + localKey + "=" + rb.getString(key));
                mResourceMap.put(localKey, rb.getString(key));// todo: use rb.getObject?
            }
        }
        //tool.setToolName( VueResources.getString(( pName+".name") ) );
    }

    public String getAttribute(String key)
    {
        return (String) mResourceMap.get(key);
    }

    /** Initialize tool from resource properties.  Must be called after setID. */
    void initFromResources()
    {
        setToolName(getAttribute("name"));
        setToolTipText(getAttribute("tooltip"));

        Icon rawIcon =  VueResources.getImageIcon(mID+".raw");
        if (rawIcon != null) {
            setGeneratedIcons(rawIcon);
        } else {
            Icon i;
            if ((i = VueResources.getImageIcon(mID+".up")) != null)               setIcon(i);
            if ((i = VueResources.getImageIcon(mID+".down")) != null)             setDownIcon(i);
            if ((i = VueResources.getImageIcon(mID+".selected")) != null)         setSelectedIcon(i);
            if ((i = VueResources.getImageIcon(mID+".disabled")) != null)         setDisabledIcon(i);
            if ((i = VueResources.getImageIcon(mID+".rollover")) != null)         setRolloverIcon(i);
            if ((i = VueResources.getImageIcon(mID+".menu")) != null)             setMenuItemIcon(i);
            if ((i = VueResources.getImageIcon(mID+".menuselected")) != null)     setMenuItemSelectedIcon(i);
        }
        
        setShortcutKey(VueResources.getChar(mID+".shortcutKey"));
        //setActiveWhileDownKey(VueResources.getChar(mID+".activeWhileDownKey")); // need to parse key code's...

        int cursorID = VueResources.getInt(mID+".cursorID", -1);
        if (cursorID >= 0) {
            //System.out.println(tool + " found cursor ID: " + cursorID);
            setCursorByID(cursorID);
        } else {
            Cursor cursor = VueResources.getCursor(mID+".cursor");
            if (cursor != null)
                setCursor(cursor);
            /*
              ImageIcon icon = VueResources.getImageIcon( mID+".cursor");
              if (icon != null) {
              //System.out.println(tool + " found cursor icon: " + icon);
              //System.out.println(tool + " cursor icon image: " + icon.getImage());
              Toolkit toolkit = Toolkit.getDefaultToolkit();
              //System.out.println("Creating cursor for " + icon);
              tool.setCursor(toolkit.createCustomCursor(icon.getImage(), new Point(0,0), mID+":"+icon.toString()));
              }
            */
        }
    }

    public void setGeneratedIcons(Icon pIcon) {
        //System.out.println(this + " generating icons from " + pIcon);
        mRawIcon = pIcon;
        setIcon(new ToolIcon(mRawIcon, ToolIcon.UP));
        setDownIcon(new ToolIcon(mRawIcon, ToolIcon.PRESSED));
        setSelectedIcon(new ToolIcon(mRawIcon, ToolIcon.SELECTED));
        setDisabledIcon(new ToolIcon(mRawIcon, ToolIcon.DISABLED));
        setRolloverIcon(new ToolIcon(mRawIcon, ToolIcon.ROLLOVER));
        setMenuItemIcon(new ToolIcon(mRawIcon, ToolIcon.MENU));
        setMenuItemSelectedIcon(new ToolIcon(mRawIcon, ToolIcon.MENU_SELECTED));
    }
    
    /** @return true if the tool requests a button the toolbar */
    public boolean hasToolbarButton() {
        return mUpIcon != null;
    }
    
    public void setLinkedButton(AbstractButton b) {
        mLinkedButton = b;
    }

    public String getSelectionID() {
        String id = getID();
        if( getSelectedSubTool()  != null ) {
            id = getSelectedSubTool().getID();
        }
        return id;
    }
 
    public String getToolName() {
        return mToolName;
    }
	
    public void setToolName( String pName ) {
        mToolName = pName;
    }

    public String getToolTipText() {
        if (mToolTipText == null)
            return null;
        else if (getShortcutKey() == 0)
            return mToolTipText;
        else 
            return mToolTipText + " (" + getShortcutKey() + ")";
    }

    public void setToolTipText( String pText ) {
        mToolTipText = pText;
    }

    public void setCursorByID(int id) {
        if (id < 0 || id > 13)
            throw new IllegalArgumentException("cursor id outside range 0-13: " + id);
        setCursor(Cursor.getPredefinedCursor(id));
    }

    public void setCursor(Cursor pCursor) {
        mCursor = pCursor;
    }

    public Cursor getCursor()
    {
        Cursor cursor = null;
        if (hasSubTools()) 
            cursor = getSelectedSubTool().getCursor();
        if (cursor == null)
            cursor = mCursor;
        return cursor;
    }

    public void setShortcutKey(char pChar) {
        mShortcutKey = pChar;
    }
    public char getShortcutKey() {
        return mShortcutKey;
    }

    public char getBackwardCompatShortcutKey() {
        return 0;
    }
    

    public void setActiveWhileDownKeyCode(int keyCode) {
        mActiveWhileDownKeyCode = keyCode;
    }
    public int getActiveWhileDownKeyCode() {
        return mActiveWhileDownKeyCode;
    }
    
    public Object getSelectionType() { return null; }
	
    public void setParentTool( VueTool pTool) {
        mParentTool = pTool;
    }
	
    public VueTool getParentTool() {
        return mParentTool;
    }
	
	
    public void setSelectedSubTool( VueTool pTool) {
        if (DEBUG.TOOL) out("setSelectedSubTool: " + pTool);
        if (DEBUG.TOOL && DEBUG.META) tufts.Util.printStackTrace("setSelectedSubTool: " + pTool);
        mSelectedSubTool = pTool;
        
//        if (VUE.getActiveViewer() !=null)
//        	VUE.getActiveViewer().setCursor(mSelectedSubTool.getCursor());
    }

    public VueTool getSelectedSubTool() {
        return mSelectedSubTool;
    }
	
    /**
     * hasSubTools
     * Determines if this tool has sub tools.
     * @return boolean - true if has subtools; false if no sub tools
     **/
    public boolean hasSubTools() {
        return !( mSubToolMap.isEmpty());
    }
	
    /**
     * getSubToolIIDs
     * Returns an ordered Vector of subtool ids for this tool
     * @return Vector - the display order set of sub tool ids
     **/
    public Vector getSubToolIDs() {
        return mSubToolIDs;
    }
	
    /**
     * getSubTool
     * Gets the subtool with the given ID.
     * @param pID the unique string ID of the subtool
     * @return VueTool the subtool with the passed ID
     **/
    public VueTool getSubTool( String pID) {
		
        Object tool = null;
        tool = mSubToolMap.get( pID);
        return (VueTool) tool;
    }
	
    /**
     * addSubTool
     * Adds the passed VueTool as a subtool of this tool
     * @param VueTool - the new subtool
     **/
    public void addSubTool(  VueTool pTool) {
        mSubToolMap.put( pTool.getID(), pTool);
        mSubToolIDs.add( pTool.getID() );
    }
	
    /**
     * removeSubTool
     * This removes teh current tool from the set of subtools
     * @param pID - the subtool 's IDto remove
     **/
    public void removeSubTool( String pID) {
        mSubToolMap.remove( pID);
        mSubToolIDs.remove( pID);
    }
	
	
    /////////////////////////////
    //
    // The follow are a slew of icon properties used for tool GUI
    //
    ////////////////////////////////////////
	
    public void setIcon( Icon pIcon) {
        mUpIcon = pIcon;
    }
	
    public Icon getIcon() {
        return mUpIcon;
    }
	
    public void setDownIcon( Icon pIcon) {
        mDownIcon = pIcon;
    }
	
    public Icon getDownIcon() {
        return mDownIcon;
    }
	
    public void setSelectedIcon( Icon pIcon) {
        mSelectedIcon = pIcon;
    }
	
    public Icon getSelectedIcon() {
        return mSelectedIcon;
    }
	
    public void setDisabledIcon( Icon pIcon) {
        mDisabledIcon = pIcon;
    }
	
    public Icon getDisabledIcon( ) {
        return mDisabledIcon;
    }
	
    public void setMenuItemSelectedIcon( Icon pIcon) {
        mMenuItemSelectedIcon = pIcon;
    }
	
    public Icon getMenuItemSelectedIcon() {
        return mMenuItemSelectedIcon;
    }
	
    public void setMenuItemIcon( Icon pIcon) {
        mMenuItemIcon = pIcon;
    }
	
    public Icon getMenuItemIcon() {
        return mMenuItemIcon;
    }
	
    public void setRolloverIcon( Icon pIcon) {
        mRolloverIcon = pIcon;
    }
	
    public Icon getRolloverIcon() {
        return mRolloverIcon;
    }
	
    public void setOverlayUpIcon( Icon pIcon) {
        mOverlayUpIcon = pIcon;
    }
	
    public Icon getOverlayUpIcon() {
        return mOverlayUpIcon;
    }
	
    public void setOverlayDownIcon( Icon pIcon) {
        mOverlayDownIcon = pIcon;
    }
	
    public Icon getOverlayDownIcon() {
        return mOverlayDownIcon;
    }

    public void out(String s) {
        //System.out.println(this + ": " + s);
        //Log.debug(getClass().getSimpleName() + ": " + s);
        Log.debug(this + ": " + s);
    }

    protected static void outln(String s) {
        System.out.println("VueTool: " + s);
    }
    static class ToolIcon extends ls.vuecp.gui.toolbar.VueButtonIcon
    {
        static final int Width = 38;
        static final int Height = 26;
        
        protected ToolIcon(Icon rawIcon, int type) {
            super(rawIcon, type, Width, Height);
            super.isRadioButton = true;
        }
    }
	
	
    public Icon getRawIcon() {
        return mRawIcon;
    }

    public String toString()
    {
        final String name =
            tufts.Util.TERM_PURPLE
            + getClass().getSimpleName() 
            + "@" + Integer.toHexString(System.identityHashCode(this))
            + tufts.Util.TERM_CLEAR
            ;
        
        
        String params = getID();

        if (getSelectedSubTool() != null)
            params += " sub=" + getSelectedSubTool();
        if (getSelectionType() != null) {
            if (params != null) params += "; ";
            params += "type=" + getSelectionType();
        }
//        if (isTemporary()) {
//            if (params != null) params += "; ";
//            params += "TEMPORARY";
//        }
        
        if (params != null)
            return name + "[" + params + "]";
        else
            return name;

    }

    
    public JPanel getContextualPanel() {
        if (mToolPanel == null)
            mToolPanel = createToolPanel();
        return mToolPanel;
    }
    
    public JPanel createToolPanel() {
        JPanel p = new JPanel();
        p.add(new JLabel(getToolName()));
        return p;
    }

}
